package com.qq.集合.Stream流;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

//目标：Stream流的常用API

//中间方法也称为非终结方法，调用完成后返回新的Stream流或流中元素（Optional）可以继续使用，支持链式编程。
//中间方法指的是：
//调用完方法之后其结果是一个新的Stream流，于是可以继续调用方法，这样一来就可以支持链式编程（或者叫流式编程）

//中间操作方法：
//filter : 过滤元素 -- Stream<T> filter(Predicate<? super T> predicate)
      
//limit : 取前几个元素--Stream<T> limit​(long maxSize) 

//skip : 跳过前几个 -- Stream<T> skip​(long n)

//map : 加工元素

//concat : 合并流。-- static <T> Stream<T> concat​(Stream a, Stream b)


//distinct : 去重复--Stream<T> distinct​()

//max : 按照自定义规则排序，取得最大值（默认最后一位）
//min : 按照自定义规则排序，取得最小值（默认第一位）
//注： 返回的是Optional<Employee>（单个元素的流）最后一定要使用get()转为对象

//sorted : 按照自定义规则排序，返回排序后的流

//注：sorted方法 有两种自定义排序规则，1）元素实现Comparable接口并重写方法，2）通过比较器对象
//	 max和min只能通过比较器对象 制定规则

//终结操作方法：
//forEach : 逐一处理(遍历)
//count : 统计个数
//      -- long count();
//collect : 收集（把流转为集合）
//终结操作方法调用后 没有返回流或流中的元素 ，所以不能再调用其他流的方法

//注：
//1.在Stream流中无法直接修改集合、数组中的数据，只是拷贝过来操作而已。
// 要操作集合和数组本身， 直接用集合和数组的API
//2.流只能使用一次！使用过后就不能再使用了，只能使用返回的新流，如果使用的是终结方法，将不会返回流。

public class Stream_Test2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
       
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张三丰");
        list.add("张强");
        list.add("张三丰");
        
        System.out.println("-------------filter方法-------------------");
//      1.filter：过滤元素 ，forEach : 逐一处理(遍历)
//        filter方法源码：  Stream<T> filter(Predicate<? super T> predicate)
//        list.stream().filter(new Predicate<String>() {
//        	
//        	@Override
//        	public boolean test(String s) {//流中有几个元素，执行几次这个方法
//        	
//        		return s.startsWith("张");//返回ture，加入到新流
//        	}
//		});
//       int s = 0;//匿名内部类里不能和匿名内部类所在方法定义的其他变量重名，也不能使用
        list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
        													
//		filter、forEach、map 三个方法原理一样，都是先遍历调用者，把遍历出来的元素传给匿名内部类中的唯一方法进行操作
//		filter会将遍历到为true的元素放到一个Stream流，把调用者元素全部遍历完后返回该Stream流
//      map会将遍历到的元素加工成别的元素然后放到一个Stream流，把调用者元素全部遍历完后返回该Stream流
//		forEach没有返回值  ，不能再调用其他方法，所以是终结方法，
        //forEach有点像没有返回值的map
        
//        注：流是一次性的
        Stream<String> aa =list.stream();
        Stream<String> bb = aa.filter(b-> b.startsWith("张"));
        //调用了方法的流就不能再使用了，需要使用方法返回的新流
//      aa.forEach(s ->System.out.println(s));
		bb.forEach(s ->System.out.println(s));
       
        
        System.out.println("----------count方法-------------");
//		2.count：统计个数 ，limit：取前几个 ，skip：跳过前几个
        long size = list.stream().filter(s -> s.length() == 3).count();
        System.out.println(size);													
       // list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(s -> System.out.println(s));
        list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(System.out::println);//方法引用
        list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(System.out::println);
        //一个流只能有一个终结操作，终结操作后无新流产生。

        System.out.println("-------------map方法---------------");
//      3.map：加工方法， 因为是"加工"，所以要用到原材料（也可以不用）。第一个参数原材料  -> 第二个参数是加工后的结果（返回值）。
//        list.stream().map(new Function<String, Student>() {//遍历调用者，将遍历出的元素传给唯一方法
//        	public Student apply(String t) {
//        		随意操作，返回回去的才是最终数据
//        		return new Student(t);//返回加工后的结果，放到流里
//        	};	
//		});
        
        //给集合元素的前面都加上一个"黑马的："
        list.stream().map(s -> "黑马的：" + s).forEach(a -> System.out.println(a));

        //把所有的名称 都加工成一个学生对象。
         list.stream().map(s -> new Student(s)).forEach(s -> System.out.println(s));
//        list.stream().map(Student::new).forEach(System.out::println); 
         				//构造器引用  					方法引用
//         注：只要lambda表达式里箭头前的参数和箭头后方法里的参数一样（包括构造器方法），就可以简写成这种形式

         System.out.println("----------------concat方法-----------------------");
//		4.concat：合并流
        Stream<String> s1 = list.stream();
        Stream<String> s2 = Stream.of("cva1", "bava2","ajkl");
        // public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
        //注：s3的泛型变量的类型必须是s1和s2的父类或本身。如果s1和s2泛型变量类型不同，则s3的泛型变量类型必须是他们的父类
        Stream<String> s3 = Stream.concat(s1 , s2);
        //注：一旦合并之后，之前的流就无法使用了
        
        System.out.println("----------------sorted方法---------------------");
//        5.sorted ： 排序 —— 不制定规则默认自然排序，自定义对象必须制定规则，方式如下：
//        1）创建比较器对象Comparator，传入sorted方法，按照自定义规则排序，返回排序后的流
//        s3.sorted((o1,o2) ->o1.charAt(0) - o2.charAt(0)).forEach(s -> System.out.println(s));
        
//        2）需要排序的元素实现Comparable接口，重写compareTo方法	，直接调用sorted方法			
        List<Student> list11 = new ArrayList<>();
        Collections.addAll(list11, new Student("日天",20) ,new Student("日地",40));
        list11.stream().sorted().forEach(s -> System.out.println(s));
        
        System.out.println("------------------max方法----------------------");
//        6.max ： 按照自定义规则排序，取得最大值（默认最后一位）
//       	 注：max方法只能通过创建比较器对象Comparator自定义规则排序
        String max = s3.max((o1,o2) ->o2.charAt(0) - o1.charAt(0)).get();
                                          //不用get()方法返回的是Optional<Employee>（单个元素的流）
        System.out.println(max);
        
        System.out.println("------------------distinct方法-------------------------");
//      7.distinct：去重复
        list.stream().distinct().forEach(s -> System.out.println(s));//去掉后面的张三丰（集合是删除第一次出现的）
       
        //自定义类型的对象，是根据哈希值和equals去重复,必须都要重写，比较内容（和set数组去重原理很像）
        List<Student> list22 = new ArrayList<>();
        Collections.addAll(list22, new Student("hh",11) ,new Student("hh",11));
        
        list22.stream().distinct().forEach(System.out::println);
        System.out.println(list22.get(0).equals(list22.get(1)));
        
    }
}

class Student implements Comparable<Student>{
	private String name;
	private int age;

	

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Student(String name) {
		super();
		this.name = name;
	}

	public Student() {
		super();
		// TODO Auto-generated constructor stub
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public int compareTo(Student o) {
		// TODO Auto-generated method stub
		
		return o.age - this.age;
	}
	
}
